package com.sinder.campus_prophet.core.contract;

import com.sinder.campus_prophet.core.annotation.RequestLimit;
import com.sinder.campus_prophet.exception.RequestLimitException;
import com.sinder.campus_prophet.utils.IPUtils;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;

/**
 * 注解实现类
 * @Aspect：定义切面类 @Aspect+@Component
 * @Before：前置增强/通知
 * within：表示在包下
 * @annotation：表示注解设定了该参数，这里指limit参数
 * 意思是：@org.springframework.web.bind.annotation.RestController @RestController注解类下面的方法且含有limit(这里指的是注解@RequestLimit)的方法有效
 * @author v-xyingde
 * @date 2023/3/23/10:06
 */
@Aspect
@Component
@Slf4j
public class RequestLimitContract {
    private Map<String, Integer> redisTemplate = new HashMap<String, Integer>();

    @Before("within(@org.springframework.web.bind.annotation.RestController *) && @annotation(limit)") // 前置通知
    public void requestLimit(final JoinPoint joinPoint, RequestLimit limit) throws RequestLimitException {
        try {
            RequestAttributes ra = RequestContextHolder.getRequestAttributes();
            ServletRequestAttributes sra = (ServletRequestAttributes) ra;
            HttpServletRequest request = sra.getRequest();
            String ip = IPUtils.getIpAddr(request);
            String url = request.getRequestURL().toString();
            String key = "req_limit_".concat(url).concat(ip);
            if (redisTemplate.get(key) == null || redisTemplate.get(key) == 0) {
                redisTemplate.put(key, 1);
            } else {
                redisTemplate.put(key, redisTemplate.get(key) + 1);
            }
            int count = redisTemplate.get(key);
            if (count > 0) {
                Timer timer = new Timer();
                TimerTask task = new TimerTask() {    //创建一个新的计时器任务。
                    @Override
                    public void run() {
                        redisTemplate.remove(key);
                    }
                };
                timer.schedule(task, limit.time());
                //安排在指定延迟后执行指定的任务。task : 所要安排的任务。10000 : 执行任务前的延迟时间，单位是毫秒。
            }
            if (count > limit.count()) {
                log.info("用户IP[" + ip + "]访问地址[" + url + "]超过了限定的次数[" + limit.count() + "]");
                throw new RequestLimitException("请勿重复执行，稍后再试");
            }
        } catch (RequestLimitException e) {
            throw e;
        } catch (Exception e) {
            log.error("RequestLimitContract发生异常: ", e);
        }
    }
}
